package com.wuhao.code.check.model

import com.aegis.kotlin.containsAny
import com.aegis.mybatis.xmlless.AnnotatedElementDetailInfo
import com.aegis.mybatis.xmlless.AnnotationSupport
import com.aegis.mybatis.xmlless.ElementInfoBuilder
import com.aegis.mybatis.xmlless.annotations.JoinObject
import com.aegis.mybatis.xmlless.annotations.SelectedProperties
import com.aegis.mybatis.xmlless.constant.ClassNames.JAVA_COLLECTION
import com.aegis.mybatis.xmlless.constant.ClassNames.KOTLIN_COLLECTION
import com.aegis.mybatis.xmlless.constant.ClassNames.SPRING_DATA_PAGE
import com.aegis.mybatis.xmlless.model.TypeInfo
import com.aegis.mybatis.xmlless.model.XmllessTableFieldInfo
import com.aegis.mybatis.xmlless.model.XmllessTableInfo
import com.aegis.mybatis.xmlless.model.base.BaseAnnotatedElementWrapper
import com.aegis.mybatis.xmlless.model.base.BaseClassWrapper
import com.intellij.psi.*
import com.intellij.psi.impl.source.PsiClassReferenceType
import com.wuhao.code.check.*
import com.wuhao.code.check.model.base.BasePsiClassWrapper
import org.jetbrains.kotlin.asJava.classes.KtUltraLightClass
import org.jetbrains.kotlin.psi.KtClass


/**
 *
 * @author 吴昊
 * @date 2024/1/19 14:21
 * @since v0.0.0
 * @version 1.0
 */
class PsiClassWrapper(private val psiClass: PsiClass) : BasePsiClassWrapper<PsiClass>(psiClass) {

  override fun getJavaClass(): Class<*> {
    return Any::class.java
  }

  override fun getClassName(): String {
    return psiClass.qualifiedName!!
  }

  override fun initXmllessTableInfo(): XmllessTableInfo {
    val tableName = KtClassWrapper.getTableName(psiClass) ?: error("无法获取表名")
    val keyField = psiClass.allFields.find {
      it.hasAnyAnnotation(*AnnotationSupport.ID_ANNOTATIONS)
    }
    val fieldList = resolveTableFields(classElement)
    return XmllessTableInfo(
        keyField?.name,
        getKeyColumn(keyField),
        tableName,
        getClassName(),
        fieldList,
        fieldList.associateBy { it.property },
        fieldList.find {
          it.isLogicDelete
        },
        createFields()

    )
  }

  override fun isComplex(): Boolean {
    return psiClass.isComplexType()
  }

  override fun getAllSuperClassesOrInterfaces(): List<String> {
    return psiClass.getAllSuperClassNames().toList()
  }

  override fun isPrimitive(): Boolean {
    return psiClass.isPrimitive()
  }

  override fun isArray(): Boolean {
    return false
  }

  override fun isCollection(): Boolean {
    return getAllSuperClassesOrInterfaces().contains(
        JAVA_COLLECTION
    )
  }

  override fun getRealType(): BaseClassWrapper {
    return PsiClassWrapper(psiClass)
  }

  override fun isString(): Boolean {
    return psiClass.getFullName() == "java.lang.String"
  }

  override fun getAllFields(): List<AnnotatedElementDetailInfo> {
    return if (this.isComplex()) {
      val result = mutableListOf<BaseAnnotatedElementWrapper>()
      if (psiClass is KtUltraLightClass) {
        val fields = psiClass.kotlinOrigin.getAllFields()
        result.addAll(fields.map { it.toAnnotationElementWrapper() })
      } else {
        psiClass.allFields.forEach {
          result.add(PsiFieldWrapper(it))
        }
      }
      result.map { ElementInfoBuilder.build(it) }
    } else {
      listOf()
    }
  }

  override fun getClassFields(): List<PsiElement> {
    return classElement.allFields.toList()
  }

  override fun resolveTableFields(
      modelType: PsiClass
  ): List<XmllessTableFieldInfo> {
    return modelType.allFields.map {
      createFieldByProperty(it)
    }
  }

  override fun <E : PsiElement> createFieldByProperty(
      field: E
  ): XmllessTableFieldInfo {
    field as PsiField
    val property = field.name
    val selectedProperties = field.getStringListAnnotationValue(
        SelectedProperties::class.java.name, "value"
    )
    val genericType = resolveGenericType(field)
    val joinObject = if (field.hasAnnotation(JoinObject::class.java)) {
      PsiElementJoinObjectProvider(field, genericType, selectedProperties, property)
    } else {
      null
    }
    return XmllessTableFieldInfo(
        PsiTableFieldWrapper(field),
        joinObject,
        genericType
    )
  }

  override fun equals(other: Any?): Boolean {
    if (this === other) {
      return true
    }
    if (javaClass != other?.javaClass) {
      return false
    }

    other as PsiClassWrapper

    return psiClass == other.psiClass
  }

  override fun hashCode(): Int {
    return psiClass.hashCode()
  }

  private fun resolveGenericType(field: PsiField): TypeInfo {
    var type = field.type
    if (type is PsiArrayType) {
      type = type.componentType
    }
    if (type is PsiPrimitiveType) {
      val realType = type.getBoxedType(field)?.resolve()
      if (realType is PsiClass) {
        return TypeInfo(Any::class.java, PsiClassWrapper(realType))
      }
    }
    if (type !is PsiClassReferenceType) {
      error("无法解析${field.name}的类型")
    }
    val realType = resolveRealType(type, field.name)
    return TypeInfo(
        Any::class.java,
        when (realType) {
          is PsiClass -> {
            PsiClassWrapper(realType)
          }

          is KtClass  -> {
            KtClassWrapper(realType)
          }

          else        -> {
            error("无法解析${field.name}的类型")
          }
        }
    )
  }

}

fun resolveRealType(type: PsiClassReferenceType, ownerName: String): PsiElement? {
  val superClasses = type.resolve()?.getAllSuperClassNames() ?: listOf()
  return if (superClasses.containsAny(listOf(KOTLIN_COLLECTION, JAVA_COLLECTION, SPRING_DATA_PAGE))) {
    val arg = type.typeArguments().first()
    if (arg is PsiClassReferenceType) {
      arg.reference.resolve()
    } else if (arg is PsiWildcardType && arg.bound is PsiClassReferenceType) {
      (arg.bound as PsiClassReferenceType).resolve()
    } else {
      error("无法解析${ownerName}的类型")
    }
  } else {
    type.resolve() ?: error("无法解析${ownerName}的类型")
  }
}
